;+
; NAME:
;		IDLIRAF
;
; PURPOSE:
;		Run an IRAF task.
;
; CALLING SEQUENCE:
;       IDLIRAF, procName, key, pipe=pipe
;
; INPUTS:
;		PROCNAME: the IRAF task name (e.g., phot)
;		KEY: structure containing relevant task parameters for the IRAF task.
;
; OPTIONAL INPUTS:
;		PIPE: string specifying the name of any output files. For example, the PHOT task will
;			return a file containing photometry to the file specified by pipe.
;
; SUBROUTINES:
;		IRAFPUTVAL: 'irafPutVal, struct, tagName, Value'
;		Recursively searches through the struct for a given structure tagName.
;		When located, the Value is stored there.
;
; 		READPARFILE: readParFile(procName, pSet, path)
;		Search for the parameter file for a given IRAF procedure (procName).
;
;		IRAFPAR: 'irafPar(procName, path, pardir=pardir)'
;		Check irafpar.dat for the path and parameter information for a given task.
;
;		GENCLFILE: 'genClFile(procName, path, params, pipe=pipe)'
;		Generate a .cl file to run
;
;		RUNCLFILE: 'runClFile, clFile, pipe, keep=keep'
;		Run the previously generate cl file, and pipe the output to pipe if specified.
;
; NOTES:
;		Must set IDL_IRAF variable in the shell to point to the directory containing a file named
;		irafpar.dat that contains relevant parameter information for each task.
;		This file does not contain information on every task, instead I have added these as
;		needed. If you need to add a new task, just add the relevant information using the 
;		following syntax;
;		IRAF_package_path 		Task_name 	Task_parameter_file
;		E.g., for PHOT, the entry would be:
;		noao.digiphot.daophot	phot		datphot.par
;
; MODIFICATION HISTORY:
;		Jacob Arnold, UC Santa Cruz, October 25, 2012
;-
;-----------------------------------------------------------------------------------------------

FUNCTION irafPar, procname, path, pardir=pardir
	COMPILE_OPT idl2, HIDDEN 
	spawn, 'echo $IDL_IRAF', pardir
	strlen = strlen(pardir)
	if (strlen eq 0) then stop else pardir = pardir[0]
	;given an IRAF procedure find the associated parameter filename
	readcol, pardir+'irafpar.dat', paths, names, parfilenames, format='a,a,a', /silent
	match = where(procname eq names, nmatch)
	if (nmatch eq 0) then begin
		print, 'irafpar.dat does not contain an entry for this procedure'
		print, 'please add the name of the associated par file to irafpar.dat and rerun'
		stop
	endif else begin
		parfilename = parfilenames[match]
		path = paths[match]
	endelse
	return, parfilename
END

;-----------------------------------------------------------------------------------------------

FUNCTION genClFile, taskname, path, param, pipe=pipe, dpar=dpar
;--Generate the cl file
	COMPILE_OPT idl2, HIDDEN
	clfile = taskname + '.cl'
	openw, lun, clfile, /get_lun
	printf, lun, '#!/iraf/irafbin/bin.macosx/cl.e'
	pkgPath = strsplit(path, '.', /extract)
	for i=0,(n_elements(pkgPath) - 1) do printf, lun, pkgPath[i]								;load the iraf package set by path
	if (n_elements(pipe) eq 0) then begin
		pipe = 'scratchFile'
		for i=0,3 do pipe = pipe + strtrim(randomu(seed),2)										;generate a random scratchfile name
	endif
	dummy = file_search(pipe, count=count)														;secondary check to make sure it doesn't exist
	if (count gt 0) then spawn, 'mv ' + pipe + ' ' + pipe + '.old'								;if it does, move the old one
	if (n_elements(param) gt 0) then clwrite, param, path, taskname, lun						;write the parameters to file
	if (n_elements(dpar) eq 0) then	printf, lun, taskname + ' > ' + pipe $						;run the task
		else printf, lun, 'dpar ' + taskname + ' > ' + pipe
	file_delete, pipe, /allow_nonexistent														;make sure the pipe file does not yet exist
	;printf, lun, 'sleep 0.1'																	;try a sleep command in lieu of the space skipping
	for k=0,15 do printf, lun, ''																;skip a few spaces so the task doesn't get tripped up	
	printf, lun, 'logout'																		;log out of cl
	free_lun, lun
	return, clfile																				;return the name of the cl file
END

;-----------------------------------------------------------------------------------------------

FUNCTION readParFile, procname, pset, path
	parfile = 'uparm/uparm'+irafpar(procname, path, pardir=pardir)								;get the name of the par file associated with this IRAF procedure
	file_mkdir, 'uparm'																			;make sure the uparm directory exists locally
	parfiles = file_search('uparm*par', count=count)											;search locally for any parameter files
	if (count gt 0) then file_move, parfiles, 'uparm/'											;move them into the uparm directory
	k = 0
	while 1 do begin
		if (k eq 2) then stop																	;if k = 2 then the parameter file was not found and not generated correctly
		loc = file_search(parfile, count=count)													;search for the parameter file
		if (count eq 0) then begin																;if it does not exist locally
			pipe = 'dpar'+procname+'.dat'														;	name of file to hold the mode for this task	
			clfile = genclfile(procname, path, pipe=pipe, /dpar)								;	generate a cl file with a dpar command for this task
			runclfile, clfile, pipe, /keep														;	run the cl file and pipe the results to pipe
			readcol, pipe, dpar, dummy, value, format='a,a,a', /silent							;	read in the file
			file_delete, pipe
			ind = where(strtrim(dpar) eq (procname+'.mode'), count)								;	find the entry for mode
			if (count eq 0) then stop else mode = strtrim(value[ind[0]])						;	record the value for mode
			clfile = genclfile(procname, path, $												;	generate a cl file to store this mode into the task parameter file
				create_struct('MODE', {name:'mode', type:'s', default:mode}), pipe=pipe, /dpar)
			runclfile, clfile, pipe																;	run the cl file, a parameter file should now exist
		endif else break
		++k
	endwhile
	
	numRows = file_line(parfile)																;count the number of rows
	str = strarr(numRows)																		;make an array to hold the data
	
	openr, lun, parfile, /get_lun																;open the parfile
	readf, lun, str																				;read in all the parameter data
	free_lun, lun																				;close the parfile
	
	;cycle through each row and break the parameter file into the component parts
	for i=0,(numRows - 1) do begin																;for each row
	
	if (n_elements(dpar) gt 0) then undefine, dpar												;	reinitialize dpar
		if (strlen(strcompress(str[i])) gt 0) then begin										;	make sure this row isn't empty
	
			hold = str[i]																		;	isolate a single parameter row
			foreach tag, ['name', 'type', 'mode'] do begin
				pos = strpos(hold,',')															;		find the comma at the end of the tag
				dpar = struct_addtags(dpar, create_struct(tag, strmid(hold,0,pos)))				;		store the tag value
				hold = strmid(hold,pos+1)														;		trim off the tag from the input string
			endforeach
	
			if (dpar.type eq 'pset') then begin													;	if this parameter is a subtask
				par = struct_addtags(par, create_struct(dpar.name, readparfile(dpar.name)))	;		recursively call readparfile for this subtask
			endif else begin																	;	otherwise
				dhold = strsplit(hold, '"', /extract, /regex)									;		split the string up at double quotes (")
				if (strmid(hold,strlen(hold)-1) eq '"') then begin
					prompt = '"'+dhold[-1]+'"'													;		save the prompt
					pos = strpos(hold, prompt)													;		find where the prompt starts
					hold = strmid(hold,0,pos-1)													;		keep only the string before this point
				endif else prompt = ''															;		in the dpar.name = mode case, there is no prompt
	
				if ((dpar.type ne 's') and (dpar.type ne 'f') and (dpar.type ne '*imcur') $
						and (dpar.type ne '*gcur') and (dpar.type ne 'pset') $
						and (dpar.type ne '*struct') ) then begin								;		if the default does not contain double quotes
					pos = strpos(hold,',')														;			find the next ,
					dhold = strmid(hold,0,pos)													;			isolate the default tag
					case dpar.type of															;			based on the type
						'b'		:	BEGIN
										if (strpos(dhold,')_.') eq 0) then dhold = '"' + dhold + '"';		if boolean add double quotes if default starts with )._
										dpar = struct_addtags(dpar, {default:dhold})			;				
									END
						else	:	BEGIN														;				otherwise
										IF (strtrim(dhold,2) ne 'INDEF') then BEGIN				;					if this is not an INDEF then
											case dpar.type of
												'i'		:	dpar = struct_addtags(dpar, {default:fix(dhold)		})	;if integer
												'r'		:	dpar = struct_addtags(dpar, {default:float(dhold)	})	;if real
												'gcur'	:	dpar = struct_addtags(dpar, {default:fix(dhold)		})	;if graphics cursor
											endcase
										ENDIF else dpar = struct_addtags(dpar, {default:dhold})	;				otherwise, just save this value as a string with no quotes
									END
					endcase
					hold = strmid(hold,pos+1)													;			trim off the default tag
				endif else begin
					if (strmid(hold,0,1) eq '"') then begin										;			if the first character is a double quote
						hold = strmid(hold,1)													;				trim it off
						pos = strpos(hold,'"')													;				find the next double quote
						dpar = struct_addtags(dpar, {default:'"'+strmid(hold,0,pos)+'"'})		;				surround the default value with double quotes
						hold = strmid(hold,pos+2)												;				trim off the " and ,
					endif else dpar = struct_addtags(dpar, {default:''})						;			otherwise save a null string as default
				endelse
				if (hold eq ',,') then hold = ','
				pos = strpos(hold,',')															;		find the next ,
				if (pos eq 0) then dhold = '' else dhold = strmid(hold,0,pos)					;		isolate the min if it exists, otherwise use a null string
				dpar = struct_addtags(dpar, {min:dhold})										;		store the min
				dpar = struct_addtags(dpar, {max:strmid(hold,pos+1)})							;		store the max
				dpar = struct_addtags(dpar, {prompt:prompt})									;		store the prompt
			
				par = struct_addtags(par, create_struct(dpar.name, dpar))						;		store parameter characteristics to a structure		
			endelse
		endif
	endfor
	return, par
END

;-----------------------------------------------------------------------------------------------

PRO irafputval, struct, tagname, value
	COMPILE_OPT idl2, HIDDEN
	vtags = tag_names(struct)																	;get the structure tagnames
	ind = where(tagname eq vtags, count)														;do any of these match the input tagname?
	if (count eq 1) then begin																	;if yes
		dpar = struct.(ind)
		if ((dpar.type eq 's') or (dpar.type eq 'f') or $
			(dpar.type eq '*imcur') or (dpar.type eq '*gcur') or $
			(dpar.type eq 'pset') ) $	;or (~strpos(value,')._'))) $
			then value = '"' + strtrim(value,2) + '"'											;added the strtrim(), does this mess anything up?
		struct.(ind).default = value															;	then install the associated value to the structure at the correct location
	endif else begin																			;if not
		dummy = jtag_exist(struct, tagname, loc=match)											;	find which sub-structure contains this tagname
		hold = struct.(match)																	;	store the sub-structure in passable memory
		irafPutVal, hold, tagname, value														;	recursively call this procedure until the correct location is found
		struct.(match) = hold																	;	store the result back to the input structure
	endelse
END

;-----------------------------------------------------------------------------------------------

PRO clWrite, param, path, taskname, lun
	COMPILE_OPT idl2, HIDDEN 
	ntags = n_elements((tagnames = tag_names(param)))
	for j=0,(ntags - 1) do begin																;cycle through each parameter
		if ~tag_exist(param.(j),'type',/top_level) then begin
			clWrite, param.(j), path, tagnames[j], lun											;if this parameter is another task then use recursion
		endif else begin
			printf, lun, strlowcase(taskname) + '.' + param.(j).name + ' = ' + strtrim(param.(j).default,2)	;	otherwise, print the parameter to file
		endelse
	endfor
END

;-----------------------------------------------------------------------------------------------

PRO runClFile, clfile, pipe, keep=keep
;--Run the cl file
	COMPILE_OPT idl2, HIDDEN 
	fileName = 'uparm*.par'
	files = file_search(fileName,count=nMatch)
	if (nMatch gt 0) then file_delete, files
	spawn, 'mv uparm/uparm*.par ' + './'														;Retrieve all uparm*.par files from the uparm directory
	spawn, 'cl < ' + clfile																		;Run the cl file to generate new uparm*.par files
	spawn, 'mv uparm*.par uparm/'																;Return uparm*.par files to the uparm directory
	if (n_elements(keep) eq 0) then file_delete, pipe	
END

;-----------------------------------------------------------------------------------------------

PRO idlIraf, procName, key, pipe=pipe
	compile_opt idl2, hidden	
	params = readParFile(procname, pset, path)													;get all the parameters for this task
	tagnames = tag_names(key)																	;get a list of all the input tags
	ntags = n_elements(tagnames)																;count them
	for i=0,(ntags - 1) do begin																;for each tag look for a match in vals
		if jtag_exist(params, tagnames[i], loc=match) then begin								;if this tag exists somewhere inside of vals
			if (match eq -1) then hold = params else hold = params.(match)						;	store the encompassing structure to passable memory
			irafPutVal, hold, tagnames[i], key.(i)												;	call the recursion function irafputval to put the value in the correct structure.default location
			if (match eq -1) then params = hold else params.(match) = hold						;	put the result back into the main parameter structure
		endif
	endfor
	if (n_elements(pipe) gt 0) then keep = 1
	clfile = genClFile(procname, path, params, pipe=pipe)										;generate the cl file to run this procedure
	runClFile, clfile, pipe, keep=keep															;execute the cl file
END


